﻿using CpuSimulator;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using System.Globalization;
using VisualConsole.Hubs;
namespace VisualConsole
{
    public class CpuService : IHostedService, IDisposable
    {
        private readonly IHubContext<InfoHub> _hubContext;
        private Computer _computer;
        public CpuService(IHubContext<InfoHub> hub)
        {
            _hubContext = hub;
            _computer = new Computer();
            _computer.Cpu.MemoryChange += _computer_MemoryChange;
            _computer.Cpu.RegisterChange += Cpu_RegisterChange;
            var connection = new HubConnectionBuilder().WithAutomaticReconnect()
                .WithUrl("http://localhost:5227/InfoHub")
                .Build();
            connection.On<string, string>("ReceiveMessage", (cmd, data) =>
            {
                switch (cmd)
                {
                    case "run":
                        _computer.Cpu.Run();
                        break;
                    case "runstep":
                        _computer.Cpu.RunStep();
                        break;
                    case "pause":
                        _computer.Cpu.Pause();
                        break;
                    case "reset":
                        _computer.Reset();
                        break;
                    case "pc":
                        _hubContext.Clients.All.SendAsync("ReceiveMessage", "register", $"100,{_computer.Cpu.PC.Value.ToString("X8")}");
                        break;
                    case "rom":
                        for (int i = 0; i < _computer.Rom.Size; i++)
                        {
                            _computer_MemoryChange(i, _computer.Rom[i].ToString("X2"));
                        }
                        break;
                    case "debug":
                        _computer.Cpu.Debug(data);
                        break;
                    case "setvalue":
                        _computer.Cpu.SetValue(data);
                        break;
                    case "export":
                        var exp = data.Split(",");
                        var rv = _computer.Cpu.Export(exp[0], exp[1]);
                        _hubContext.Clients.All.SendAsync("ReceiveMessage", "translate", rv);
                        break;
                    case "write":
                        List<byte> inputdata = new List<byte>();
                        var pair = data.Split('`');
                        var posstr = pair[0];
                        int pos = 0;
                        if (string.IsNullOrEmpty(posstr) == false)
                        {
                            posstr = posstr.Trim().ToLower();
                            if (posstr.StartsWith("0x"))
                            {
                                int.TryParse(posstr[2..], NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo, out pos);
                            }
                            else
                            {
                                int.TryParse(posstr, out pos);
                            }
                        }
                        else
                        {
                            pos = _computer.Cpu.PC.Value;
                        }
                        bool IsText = false;
                        if (pair[1] == "true")
                        {
                            IsText = true;
                        }
                        var lines = pair[2].Split("\n");
                        for (int a = 0; a < lines.Length; a++)
                        {
                            var item = lines[a];
                            var line = item.Trim().ToLower();
                            var commentpos = line.IndexOf("//");
                            if (commentpos != -1) { line = line.Substring(0, commentpos); }
                            if (string.IsNullOrEmpty(line))
                            {
                                continue;
                            }
                            if (IsText == true)
                            {
                                foreach (var c in line)
                                {
                                    if (c > 255)
                                    {
                                        inputdata.AddRange(BitConverter.GetBytes(c));
                                    }
                                    else
                                    {
                                        inputdata.Add((byte)c);
                                    }
                                }
                                if (a < lines.Length - 1)
                                {
                                    inputdata.Add((byte)0x0A);
                                }
                            }
                            else
                            {
                                line = line.Replace("-", "");
                                if (line.Length > 32)
                                {
                                    line = line.Substring(0, 32);
                                }
                                //如果长度在4到32之间，表示可能是2进制数据
                                if (line.Length > 8 && line.Length <= 32)
                                {
                                    //如果不够32位则补全
                                    while (line.Length < 32)
                                    {
                                        line = "0" + line;
                                    }
                                    byte b = 0;
                                    int index = 0;
                                    for (int i = line.Length - 1; i >= 0; i--)
                                    {
                                        byte ly = 0;
                                        if (line[i] == '1')
                                        {
                                            ly = 1;
                                        }
                                        b += (byte)(Math.Pow(2, index) * ly);
                                        index++;
                                        //8位一组
                                        if (i % 8 == 0)
                                        {
                                            inputdata.Add(b);
                                            b = 0;
                                            index = 0;
                                        }
                                    }
                                }
                                //否则认为是16进制输入
                                else
                                {
                                    //如果不够8个数字则补全
                                    while (line.Length < 8)
                                    {
                                        line = "0" + line;
                                    }
                                    byte b = 0;
                                    int index = 0;
                                    for (int i = line.Length - 1; i >= 0; i--)
                                    {
                                        int number = 0;
                                        char sn = line[i];
                                        switch (sn)
                                        {
                                            case >= '1' and <= '9':
                                                number = sn - 48;
                                                break;
                                            case >= 'a' and <= 'f':
                                                number = sn - 87;
                                                break;
                                            default:
                                                break;
                                        }

                                        b += (byte)(Math.Pow(16, index) * number);
                                        index++;
                                        //2个数一组
                                        if (i % 2 == 0)
                                        {
                                            inputdata.Add(b);
                                            b = 0;
                                            index = 0;
                                        }
                                    }

                                }
                            }
                        }

                        _computer.Cpu.WriteToAddress(inputdata.ToArray(), pos);
                        break;
                    case "book":
                        var pa = data.Split(",");
                        if (pa.Length == 5)
                        {
                            var result = _computer.Cpu.TranslateCmd(pa[0], pa[1], pa[2], pa[3], pa[4]);
                            _hubContext.Clients.All.SendAsync("ReceiveMessage", "translate", result);
                        }
                        break;
                    case "batchtranslate":
                        var result2 = _computer.Cpu.TranslateText(data);
                        _hubContext.Clients.All.SendAsync("ReceiveMessage", "translate", result2);
                        break;
                    default:
                        break;
                }
            });
            connection.StartAsync();
        }

        private void Cpu_RegisterChange(int arg1, string arg2)
        {
            _hubContext.Clients.All.SendAsync("ReceiveMessage", "register", $"{arg1},{arg2}");
        }

        private void _computer_MemoryChange(int arg1, string arg2)
        {
            _hubContext.Clients.All.SendAsync("ReceiveMessage", "memory", $"{arg1},{arg2}");
            if ((arg1 + 1) % 4 == 0)
            {
                string val = _computer.Cpu.Disassembly(arg1);
                _hubContext.Clients.All.SendAsync("ReceiveMessage", "comment", $"{(arg1 + 1) / 4 - 1}`{val}");
            }
        }

        public Task StartAsync(CancellationToken stoppingToken)
        {
            _computer.Start();
            return Task.CompletedTask;
        }



        public Task StopAsync(CancellationToken stoppingToken)
        {
            return Task.CompletedTask;
        }

        public void Dispose()
        {
        }
    }
}
